/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2020 All Rights Reserved.
 */
package com.smp.support.shared;

import com.smp.domain.BaseInputDomain;
import com.smp.domain.BaseOutputDomain;
import com.smp.exception.ExpoAppException;
import com.smp.util.SpringContextUtils;
import com.smp.util.TransactionUtils;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;

/**
 * 应用服务模板callback
 *
 * @author zhulang.jy
 * @version : SmpServiceCallback.java, v 0.1 2020年10月14日 16:59 zhulang.jy Exp $
 */
public abstract class SmpServiceCallback<T extends BaseInputDomain, R extends BaseOutputDomain> {

    private TransactionUtils transactionUtils;

    private ServiceContext<T, R> serviceContext;

    public SmpServiceCallback() {
        transactionUtils = SpringContextUtils.getBean(TransactionUtils.class);
    }

    public SmpServiceCallback(ServiceContext<T, R> serviceContext) {
        this.serviceContext = serviceContext;
        transactionUtils = SpringContextUtils.getBean(TransactionUtils.class);
    }

    /***
     * 前置检查:针对原始请求入参的基本校验
     *
     * @throws ExpoAppException
     */
    public abstract void preCheck() throws ExpoAppException;

    /**
     * 初始化
     */
    public abstract void init();

    /**
     * 构建请求对象领域模型
     *
     * @return 构建入参领域模型
     */
    public abstract T buildInputDomain();

    /**
     * 构建响应对象领域模型
     *
     * @return 构建出参领域模型
     */
    public abstract R buildOutputDomain();

    /**
     * 根据出参的领域模型构建业务返回结果
     *
     * @param responseDomain 出参领域模型
     * @return Object 用于打印结果日志的结果返回
     */
    public abstract Object buildResult(R responseDomain);

    /**
     * 服务简单实现，当前不需要复杂编排时直接实现本方法
     *
     * @param context 服务上下文
     */
    public abstract void simpleProcess(ServiceContext<T, R> context);

    public void doProcess(ServiceContext<T, R> context) throws NoSuchMethodException {
        Method method = this.getClass().getMethod(TransactionUtils.METHOD_PROCESS_NAME, ServiceContext.class);
        Transactional transactional = method.getAnnotation(Transactional.class);
        if (transactional != null) {
            //开启事务
            TransactionStatus transactionStatus = transactionUtils.init();
            try {
                simpleProcess(context);
                //执行成功提交事务
                transactionUtils.commit(transactionStatus);
            } catch (Throwable throwable) {
                //执行方法出错则回滚事务
                transactionUtils.rollback(transactionStatus);
                throw throwable;
            }
        } else {
            simpleProcess(context);
        }
    }

    public ServiceContext<T, R> context() {
        return this.serviceContext;
    }

}